package com.fishbone.jwt;

import com.alibaba.fastjson.JSONException;
import com.fishbone.jwt.config.JwtProperties;
import com.fishbone.jwt.exceptions.ResultException;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;

import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.Map;

/**
 * @ClassName RSATokenGenerator
 * @Author huyaxi
 * @DATE 2020/4/2 9:57
 */
public class RSATokenGenerator implements JwtTokenGenerator{

    private JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();

    private JwtProperties jwtProperties;

    private JwtTokenStorage jwtTokenStorage;

    private KeyPair keyPair;

    public RSATokenGenerator(JwtTokenStorage tokenStorage,JwtProperties jwtProperties,KeyPair keyPair){
        this.jwtTokenStorage = tokenStorage;
        this.jwtProperties = jwtProperties;
        this.keyPair = keyPair;
    }


    @Override
    public JwtTokenPair jwtTokenPair(String aud,Map<String, Object> additional) {
        String accessToken = jwtToken(aud,additional);
        String refreshToken = jwtToken(aud,additional);
        JwtTokenPair jwtTokenPair = new JwtTokenPair();
        jwtTokenPair.setAccessToken(accessToken);
        jwtTokenPair.setRefreshToken(refreshToken);
        jwtTokenStorage.put(jwtTokenPair,aud);
        return jwtTokenPair;
    }

    /**
     * 生成Token
     * @param aud
     * @param additional
     * @return
     */
    private String jwtToken(String aud,Map<String, Object> additional){
        try {
            builder.expirationTime(new Date(System.currentTimeMillis() + jwtProperties.getAccessExpTime()))
                    .subject(jwtProperties.getSub())
                    .audience(aud)
                    .issuer(jwtProperties.getIss());
            additional.forEach(builder::claim);
            JWTClaimsSet claimsSet = builder.build();
            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) this.keyPair.getPrivate();
            RSASSASigner rsassaSigner = new RSASSASigner(rsaPrivateKey);
            SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet);
            signedJWT.sign(rsassaSigner);
            return signedJWT.serialize();
        }catch (JSONException | JOSEException e){
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public JWTClaimsSet decodeAndVerify(String token) {
        try {
            SignedJWT jwt = SignedJWT.parse(token);
            RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic();
            RSASSAVerifier rsassaVerifier  = new RSASSAVerifier(publicKey);
            //校验是否有效
            if (!jwt.verify(rsassaVerifier)) {
                throw ResultException.of(-1, "Token 无效");
            }
            //校验超时
            if (new Date().after(jwt.getJWTClaimsSet().getExpirationTime())) {
                throw ResultException.of(-2, "Token 已过期");
            }
            return jwt.getJWTClaimsSet();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }


    @Override
    public String generateAud(String account, Long userId, String customerId) {
        return account+"_"+userId+"_"+customerId;
    }
}
